package luj.tool.jnproxyan.proxy.admin.http.handle.invoke;

import com.google.common.collect.ImmutableMap;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.common.template.TemplateEngine;
import luj.cluster.api.actor.Tellable;
import luj.tool.jnproxyan.framework.proxy.actor.tell.ActorTeller;
import luj.tool.jnproxyan.proxy.admin.AdminHttpHandler;
import luj.tool.jnproxyan.proxy.admin.AdminHttpModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedHashMap;
import java.util.Map;

public class HttpHandleInvoker {

  public HttpHandleInvoker(AdminHttpHandler handler, RoutingContext vertx, AdminHttpModule httpModule, TemplateEngine freeMarker, Tellable adminRef, ActorTeller actorTeller) {
    _handler = handler;
    _vertx = vertx;
    _httpModule = httpModule;
    _freeMarker = freeMarker;
    _adminRef = adminRef;
    _actorTeller = actorTeller;
  }

  public void invoke() {
    HandleContextImpl ctx = new HandleContextImpl();
    ctx._param = makeParam();
    ctx._adminRef = _adminRef;
    ctx._actorTeller = _actorTeller;

    ctx._vertx = _vertx;
    ctx._freeMarker = _freeMarker;
    ctx._rootModule = getRootModule();

    HttpServerRequest req = _vertx.request();
    try {
      LOG.debug("{} {} {} {}", req.remoteAddress(),
          req.method(), _vertx.normalizedPath(), ctx._param);

      _handler.onHandle(ctx);

    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new UnsupportedOperationException(e);
    }
  }

  private Map<String, Object> makeParam() {
    HttpServerRequest req = _vertx.request();
    Map<String, Object> result = new LinkedHashMap<>();

    for (Map.Entry<String, String> e : _vertx.pathParams().entrySet()) {
      result.put(":" + e.getKey(), e.getValue());
    }

    MultiMap reqParam = req.params();
    for (String name : reqParam.names()) {
      if (result.containsKey(":" + name)) {
        continue;
      }
      result.put(name, reqParam.get(name));
    }

    return ImmutableMap.copyOf(result);
  }

  private AdminHttpModule getRootModule() {
    AdminHttpModule cursor = _httpModule;
    while (cursor.parent() != null) {
      cursor = cursor.parent();
    }
    return cursor;
  }

  private static final Logger LOG = LoggerFactory.getLogger(HttpHandleInvoker.class);

  private final AdminHttpHandler _handler;
  private final RoutingContext _vertx;

  private final AdminHttpModule _httpModule;
  private final TemplateEngine _freeMarker;

  private final Tellable _adminRef;
  private final ActorTeller _actorTeller;
}
